package xj.graph2d.template;

import java.awt.Color;
import java.io.BufferedWriter;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;

import javax.swing.Icon;

import xj.graph2d.DrawAttr;
import xj.graph2d.DrawShape;
import xj.graph2d.DrawShapeConstants;
import xj.graph2d.GraphEdgeBase;
import xj.graph2d.OpenShape;
import xj.graph2d.TextShape;
import xj.graph2d.util.DrawingUtilities;
import xj.lang.CodeWriter;
import xj.util.gui.ColorUtil;

public abstract class Template {

  // template style
  public static int allTemplateStyle     = 0;  
  public static int classicTemplateStyle = 1;  
  public static int modernTemplateStyle  = 2;  

  public boolean gradientBackground = true; 

  public Color defaultPenColor = Color.black;

  public Color defaultFillColor = Color.white;

  public Color defaultTextColor = Color.black;

  public int defaultBorderWidth = 1;

  public int defaultEdgeWidth = 1;

  public boolean dropShadow = true; 

  public int templateStyle = allTemplateStyle;  

  public Template(boolean graphComp) {
    this.graphComp = graphComp;
    // makeTemplate();
  }

  public Template() {
    // makeTemplate();
  }

  public void setName(String name) { 
    this.name = name; 
  }

  public String getName() { 
    return name; 
  }

  public void setTemplateStyle(int st) {
    templateStyle = st; 
  }

  public boolean isClassicTemplate() { 
    return (templateStyle == classicTemplateStyle || 
	    templateStyle == allTemplateStyle); 
  }

  public boolean isModernTemplate() { 
    return (templateStyle == modernTemplateStyle || 
	    templateStyle == allTemplateStyle); 
  }

  public void initTools() {}

  abstract public void makeTemplate(Map<String, Icon> iconMap);

  public void addDefaultStereotypes() {
  }

  public void add(TemplateToolButton button) {
    if (button != null) {
      toolList.add(button);
    }
  }

  public void addEdgeShapeTemplate(String name, GraphEdgeBase edge, 
				   int iconWidth, int iconHeight) { 
    if (name != null && 
	edge != null) { 
      //System.out.println("Template.addEdgeShapeTemplate() name=" + name); 
      
      edgeTemplateList.add(new GraphEdgeTemplate(name, edge, iconWidth, iconHeight));
    }
  }

  public GraphEdgeTemplate getEdgeShapeTemplate(String name) {
    if (name != null) {
      Iterator<GraphEdgeTemplate> iter = edgeTemplateList.iterator();
      while (iter.hasNext()) {
	GraphEdgeTemplate t = iter.next();
	if (name.equals(t.name)) {
	  return t;
	}
      }
    }
    return null;
  }

  public TemplateToolButton getButton(String name) {
    if (name != null) {
      Iterator<TemplateToolButton> iter = toolList.iterator();
      while (iter.hasNext()) {
	TemplateToolButton b = iter.next();
	if (name.equals(b.name)) {
	  return b;
	}
      }
    }
    return null;
  }

  public DrawShape getPrototype(String name) {
    if (name != null) {
      Iterator<TemplateToolButton> iter = toolList.iterator();
      while (iter.hasNext()) {
	TemplateToolButton b = iter.next();
	if (name.equals(b.name)) {
	  return b.shape;
	}
      }
    }
    return null;
  }

  public List<TemplateToolButton> getButtonList(int buttonType) {
    List<TemplateToolButton> result = new ArrayList<TemplateToolButton>(toolList.size()); 
    for (int i = 0; i < toolList.size(); i++) { 
      TemplateToolButton b = toolList.get(i);
      if (b.buttonType == buttonType) { 
	result.add(b);
      }
    }
    return result; 
  }

  public void putIcon(String name, Icon icon) { 
    if (name != null && 
	icon != null) { 
      icons.put(name, icon); 
    }
  }

  public Icon getIcon(String name) { 
    if (name != null) { 
      return icons.get(name);
    }
    return null;
  }

  public boolean isGraphComponent() {
    return graphComp;
  }

  public void setGraphComponent(boolean graphComp) {
    this.graphComp = graphComp;
  }

  public void setStyleOptions(Properties styleOptions) {
    this.styleOptions = styleOptions;
  }

  // hover popup when pointer is not over any shape 
  public String[][] getGapToolNames() { 
    return null;
  }

  // hover popup when pointer is over a node, near a side, tools for each of four sides
  public static String[][] getNodeToolNames(String nodeTempName) { 
    if (nodeTempName != null) { 
      return nodeToolMap.get(nodeTempName);
    }
    return null;
  }

  public static void addNodeToolNames(String nodeTempName, String[][] nodeToolNames) { 
    if (nodeTempName != null && 
	nodeToolNames != null) { 
      nodeToolMap.put(nodeTempName, nodeToolNames);
    }
  }

  // popup when pointer is over an edge 
  public static String[] getEdgeToolNames(String edgeTempName) { 
    if (edgeTempName != null) { 
      return edgeToolMap.get(edgeTempName);
    }
    return null;
  }

  public static void addEdgeToolNames(String edgeTempName, String[] edgeToolNames) { 
    if (edgeTempName != null && 
	edgeToolNames != null) { 
      edgeToolMap.put(edgeTempName, edgeToolNames);
    }
  }

  // hover popup when pointer is over the interior of a shape
  public static String[] getShapeToolNames(String nodeTempName) { 
    if (nodeTempName != null) { 
      return shapeToolMap.get(nodeTempName);
    }
    return null;
  }

  public static void addShapeToolNames(String nodeTempName, String[] shapeToolNames) { 
    if (nodeTempName != null && 
	shapeToolNames != null) { 
      shapeToolMap.put(nodeTempName, shapeToolNames);
    }
  }

  // hover popup when pointer is over a block shape in visual program  
  public static String[] getSubnodeToolNames(String nodeTempName) { 
    if (nodeTempName != null) { 
      return subnodeToolMap.get(nodeTempName);
    }
    return null;
  }

  public static void addSubnodeToolNames(String nodeTempName, String[] subnodeToolNames) { 
    if (nodeTempName != null && 
	subnodeToolNames != null) { 
      subnodeToolMap.put(nodeTempName, subnodeToolNames);
    }
  }

  public DrawShape setFillColor(DrawShape shape, Color color) {
    if (shape != null && 
	color != null) { 
      DrawAttr attr = shape.getDrawAttr();
      if (attr == null) {
	attr = new DrawAttr();
	shape.setDrawAttr(attr);
      }
      attr.setFillColor(color);
    }
    return shape;
  }

  public DrawShape setPenColor(DrawShape shape, String key) {
    if (shape != null && 
	key != null && 
	styleOptions != null) {
      Object val = styleOptions.get(key);
      if (val instanceof String) {
	String colorname = (String) val;
	//System.out.println("Template.setPenColor() colorname=" + colorname);

	Color color = ColorUtil.getColor(colorname, null);
	if (color != null) {
	  DrawAttr attr = shape.getDrawAttr();
	  if (attr == null) {
	    attr = new DrawAttr();
	    shape.setDrawAttr(attr);
	  }
	  attr.setPenColor(color);
	}
      }
    }
    return shape;
  }

  public DrawShape setNodeAttr(DrawShape shape) {
    return setNodeAttr(shape, DrawAttr.SOLID_LINE, false);
  }

  public DrawShape setNodeAttr(DrawShape shape, boolean filled) {
    return setNodeAttr(shape, DrawAttr.SOLID_LINE, filled);
  }

  public DrawShape setNodeAttr(DrawShape shape, int lineStyle) {
    return setNodeAttr(shape, lineStyle, false);
  }

  public DrawShape setNodeAttr(DrawShape shape, int lineStyle, boolean filled) {
    if (shape != null) {
      DrawAttr attr = new DrawAttr();
      attr.setLineStyle(lineStyle);
      attr.setPenSize(defaultBorderWidth);
      if (filled) {
	shape.setFilled(true);
	attr.setFillColor(defaultFillColor);
      }
      shape.setDrawAttr(attr);
      if (!(shape instanceof OpenShape) && 
	  !(shape instanceof TextShape)) {
	shape.setShadow(dropShadow);
	shape.setAllowRotation(false);
	shape.setSymetricResize(false);
      }
    }
    return shape;
  }

  protected Color getColor(String key) {
    if (key != null && 
	styleOptions != null) {
      Object val = styleOptions.get(key);
      if (val instanceof String) {
	String colorname = (String) val;
	String akey = null;
	int i = colorname.indexOf(',');
	if (i > 0) {
	  akey = colorname.substring(i + 1);
	  colorname = colorname.substring(0, i);
	}
	Color c = ColorUtil.getColor(colorname, null);
	if (c != null) {
	  if (akey == null) {
	    return c;
	  } else {
	    int a;
	    try {
	      a = Integer.parseInt(akey);
	    } catch (NumberFormatException e) {
	      a = 255;
	    }
	    if (a > 255 || a < 0) {
	      a = 255;
	    }
	    Color color = new Color(c.getRed(), c.getGreen(), c.getBlue(), a);
	    return color;
	  }
	}
      }
    }
    return null;
  }

  public DrawShape setNodeColor(DrawShape shape, 
				String fgColorKey,
				String bgColorKey) {
    return setNodeColor(shape, fgColorKey, bgColorKey, true);
  }

  public DrawShape setNodeColor(DrawShape shape, 
				String fgColorKey,
				String bgColorKey,
				boolean horizontal) {
    if (shape != null && 
	styleOptions != null) {
      shape.setFilled(true);
      DrawAttr attr = shape.getDrawAttr();
      if (attr == null) {
	attr = new DrawAttr();
	shape.setDrawAttr(attr);
      }
      setColor(attr, fgColorKey, bgColorKey, horizontal);
    }
    return shape;
  }

  public void setColor(DrawAttr attr, 
		       String fgColorKey,
		       String bgColorKey) {
    setColor(attr, fgColorKey, bgColorKey, true);
  }

  public void setColor(DrawAttr attr, 
		       String fgColorKey,
		       String bgColorKey,
		       boolean horizontal) {
    if (attr != null && 
	styleOptions != null) {
      Color fgColor = getColor(fgColorKey);
      if (fgColor != null) {
	attr.setPenColor(fgColor);
      }
      if (bgColorKey == null) { 
	attr.setFillColor(null);
      } else { 
	Color bgColor = getColor(bgColorKey);
	if (bgColor != null) {
	  attr.setFillColor(bgColor);
	}
      
	if (gradientBackground) { 
	  Color gradientColor1 = bgColor;
	  Color gradientColor2 = DrawingUtilities.darkerColor(bgColor, 0.7f);
	  attr.setGradientColor1(gradientColor1);
	  attr.setGradientColor2(gradientColor2);
	  if (horizontal) { 
	    attr.setGradientMode(DrawAttr.LINEAR_GRADIENT_LEFT_TO_RIGHT);
	  } else { 
	    attr.setGradientMode(DrawAttr.LINEAR_GRADIENT_TOP_TO_BOTTOM);
	  }
	}
      }      
    }
  }

  public DrawShape setNodeBgColor(DrawShape shape, String key) {
    return setNodeBgColor(shape, key, false); 
  }

  public DrawShape setNodeBgColor(DrawShape shape, String key, boolean radial) {
    if (shape != null && 
	key != null && 
	styleOptions != null) {
      Color color = getColor(key);
      if (color != null) {
	DrawAttr attr = shape.getDrawAttr();
	if (attr == null) {
	  attr = new DrawAttr();
	  shape.setDrawAttr(attr);
	}
	shape.setFilled(true);
	attr.setFillColor(color);

	if (gradientBackground) { 
	  if (radial) { 
	    Color gradientColor1 = DrawingUtilities.lighterColor(color, 150);
	      //Color.white; 
	    Color gradientColor2 = color;
	    attr.setGradientColor1(gradientColor1);
	    attr.setGradientColor2(gradientColor2);
	    attr.setGradientMode(DrawAttr.RADIAL_GRADIENT_CENTER);
	  } else { 
	    Color gradientColor1 = color;
	    Color gradientColor2 = DrawingUtilities.darkerColor(color, 0.7f);
	    attr.setGradientColor1(gradientColor1);
	    attr.setGradientColor2(gradientColor2);
	    attr.setGradientMode(DrawAttr.LINEAR_GRADIENT_LEFT_TO_RIGHT);
	  }
	}
      }
    }
    return shape;
  }

  public DrawShape setNodeBgColor(DrawShape shape, Color color) {
    if (shape != null && 
	color != null) {
      DrawAttr attr = shape.getDrawAttr();
      if (attr == null) {
	attr = new DrawAttr();
	shape.setDrawAttr(attr);
      }
      shape.setFilled(true);
      attr.setFillColor(color);

      if (gradientBackground) { 
	Color gradientColor1 = color;
	Color gradientColor2 = DrawingUtilities.darkerColor(color, 0.7f);
	attr.setGradientColor1(gradientColor1);
	attr.setGradientColor2(gradientColor2);
	attr.setGradientMode(DrawAttr.LINEAR_GRADIENT_LEFT_TO_RIGHT);
      }
    }
    return shape;
  }

  public DrawShape makeDocumentHeader(String h) { 
    TextShape text = new TextShape(h); 
    text.setPrompt("header");
    text.setRole(DrawShapeConstants.ROLE_DOCUMENT_HEADER);
    text.setAllowMultiLine(false);
    text.setDetachable(false);
    text.setScalable(false);
    text.setSticky(true);
    text.setEditable(false);
    text.showCursor(false);
    return text;
  }

  public void write(String filename) throws IOException {
    OutputStream out = new FileOutputStream(filename);
    PrintWriter pout = new PrintWriter(new BufferedWriter(new OutputStreamWriter(out)));
    CodeWriter cout = new CodeWriter(pout);
    cout.setIndentSize(2);
    cout.println("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\" ?>");
    cout.println("<!-- Aesthetic Draw Template " + (new Date()) + " -->");

    cout.println("<Template version=\"2.0\">");
    cout.thisIndentInc();

    int n = toolList.size();
    for (int i = 0; i < n; i++) {
      TemplateToolButton b = toolList.get(i);
      b.write(cout);
    }

    cout.thisIndentDec();
    cout.println("</Template>");
    cout.close();
  }

  protected String name; 

  protected List<TemplateToolButton> toolList = new ArrayList<TemplateToolButton>();
  protected List<GraphEdgeTemplate> edgeTemplateList = new ArrayList<GraphEdgeTemplate>();

  protected Map<String, Icon> icons = new HashMap<String, Icon>();

  protected boolean graphComp = false;

  protected Properties styleOptions = null;

  // template name => template names 

  protected static Map<String, String[][]> nodeToolMap  = new HashMap<String, String[][]>();
  protected static Map<String, String[]>   shapeToolMap = new HashMap<String, String[]>();
  protected static Map<String, String[]>   edgeToolMap = new HashMap<String, String[]>();
  protected static Map<String, String[]>   subnodeToolMap = new HashMap<String, String[]>();

}
